<?php

class Quad_Db_Select
{
	public $bGetFoundRows, $sTypeWord, $aSelectFields, $aAggregateFields, $sTable, $aJoinFields, 
	$aWhere, $aGroupBy, $aHaving, $aOrderBy, $iLimit, $iOffset;
  protected $_oDb, $_bAutoQuote;
	
	public function __construct($db = null)
	{
	  if ($db instanceof Quad_Db_Adapter_Interface) {
   		$this->_oDb = $db;
    }
    $this->reset();
	}
  
  public function getResults($bindVars=array())
  {
    return $this->_oDb->select((string) $this, $bindVars);
  }
	
	public function reset()
	{
	  $this->_bAutoQuote = false;
		$this->bGetFoundRows = null;
		$this->aSelectFields = array();
		$this->sTable = '';
		$this->aJoinFields = array();
		$this->aAndWhere = array();
		$this->aOrWhere = array();
		$this->aGroupBy = array();
		$this->aHaving = array();
		$this->aOrderBy = array();
		$this->iLimit = null;	
		$this->iOffset = null;
    return $this;
	}
  
  public function enableQuoting()
  {
    $this->_bAutoQuote = true;
    return $this;
  }
  
  public function disableQuoting()
  {
    $this->_bAutoQuote = false;
    return $this;
  }
  
  public function _quote($identifier)
  {
    if ($this->_oDb && $this->_bAutoQuote && !($identifier instanceof Quad_Db_Expr)) {
      $parts = explode('.', $identifier);
      $quotedParts = array();
      foreach ($parts as $part) {
        $quotedParts[] = $this->_oDb->quote($identifier);
      }
      $identifier = join('.',$quotedParts);
    }
    return $identifier;
  }
	
	public function column($sSelectField)
	{
		$this->aSelectFields[] = $this->_quote($sSelectField);
    return $this;
	}
		
	public function from($sTable/*col,col,col*/)
	{
	  $args = func_get_args();
    array_shift($args);
    foreach ($args as $a) {
      $this->column($a);
    }
		$this->sTable = $this->_quote($sTable);	
    return $this;
	}

	public function __call($m, $a)
	{
	  if (
      $m == 'join' ||
      preg_match('/(left|right|inner|outer|natural|cross)_?join/i',$m,$match) ||
      preg_match('/join(left|right|inner|outer|natural|cross)/i',$m,$match)      
    ) {
      if ($match) {
        $type = strtoupper($match[1]);
      }
      else {
        $type = 'INNER';
      }
  	  list ($sTable, $sCriteria) = $a;
      return $this->_join($type,$sTable,$sCriteria);
	  }
    throw new Quad_Db_Exception("Call to undefined method '$m'");
	}
  
  protected function _join($sType, $sTable, $sCriteria)
	{
		$this->aJoinFields[] = "$sType JOIN $sTable ON $sCriteria";	
    return $this;
	}  
	
	public function where( $sWhere )
	{
		return $this->andWhere($sCriteria);
	}
  
  public function orWhere($sCriteria) {
    $this->aOrWhere[] = $sCriteria;
    return $this;
  }
  
  public function andWhere($sCriteria) {
    $this->aAndWhere[] = $sWhere;
    return $this;
  }
	
	public function groupBy( $sGroupBy )
	{
		$this->aGroupBy[] = $sGroupBy;
    return this;
	}
		
	public function having( $sHaving )
	{
	  // or having, and having?
		$this->aHaving[] = $sHaving;
    return $this;
	}	
	
	public function orderBy( $sOrderBy )
	{
		$this->aOrderBy[] = $sOrderBy;
    return $this;
	}

	public function limit( $iLimit, $iOffset = null )
	{
		$this->iLimit = (int) $iLimit;
    if ($iOffset !== null) {
      $this->offset($iOffset);
    }
    return $this;
	}	

	public function offset( $iOffset )
	{
		$this->iOffset = (int) $iOffset;
    return $this;
	}
	
	public function requestFoundRows( $setTo=true )
	{
		$this->bGetFoundRows = $setTo;
    return $this;
	}
  
  public function normalize() {
    $sql = (string) $this;
    $sql = preg_replace('/\s+/',' ',$sql);
    return $sql;
  }
	
	public function __toString()
	{
	  $where = '';
    $andWhere = join(' AND ', $this->aAndWhere);
    $orWhere = join(' OR ', $this->aAndWhere);
    if ($andWhere && $orWhere) {
      $where = "$andWhere OR $orWhere";
    }
    elseif ($andWhere || $orWhere) {
      $where = "$andWhere $orWhere";
    }
    
		$sql = 
			"SELECT" . 
			($this->bGetFoundRows                ? " SQL_CALC_FOUND_ROWS\n" : "\n" ) .
			(!empty($this->aSelectFields)        ? join(",\n",$this->aSelectFields) . "\n" : '*\n' ) . 		
			"FROM $this->sTable\n" .
			(!empty($this->aJoinFields)	         ? join("\n",$this->aJoinFields) . "\n" : '' ) .
			(!empty($where)                      ? "WHERE\n" . $where . "\n" : '' ) .
			(!empty($this->aGroupBy)             ? "GROUP BY\n" . join(",\n",$this->aGroupBy) . "\n" : '' ) .
			(!empty($this->aHaving)              ? "HAVING\n" . join("\n",$this->aHaving) . "\n" : '' ) .
			(!empty($this->aOrderBy)             ? "ORDER BY\n" . join(",\n",$this->aOrderBy) . "\n" : '' ) .
			($this->iLimit && $this->iOffset     ? "LIMIT $this->iOffset, $this->iLimit" : '' ) . "\n" .
			($this->iLimit && !$this->iOffset    ? "LIMIT $this->iLimit" : '' )
    ;
		return $sql;
	}
	
}